<script setup>

import { ref, onMounted, watch, markRaw, defineAsyncComponent, nextTick, h } from "vue";
import axios from 'axios';
import login from "./pages/login.vue";

import { store, loginOpen } from './js/store.js';
import request from './js/request.js';
import {flattenTree} from "./js/function.js";
import {Router,topage} from "./js/hashRouter.js";
import recursiveMenu from "./components/recursive-menu.vue";

//后台菜单数据接口（当前是相对路径）
const api_menu = store.rootPath + 'js/admin_menu.json';

/**
 * https://gitee.com/flashme/vite-elementplus-cli
 * 路由：
 * #/home （无外框架，只显示home页）
 * #/frame/home （外框架内显示home页）
 * #/frame/articles/pid=101 （打开articles页面，后缀出现 /pid= 时打开新页面，应对同一路由打开不同缓存页）
*/
// 使用import.meta.glob批量导入views目录下所有.vue及子集目录下的*.index.vue
// 实现批量加载页面组件，并关联路由、页面可被缓存
// *.index.vue页面需要重命名组件名：defineOptions({name: '.index前的文件名）'}); 以关联缓存页面
const vueFiles = import.meta.glob(`/src/views/*.vue`);
const dirFiles = import.meta.glob(`/src/views/**/*.index.vue`);
const viewsFiles = { ...vueFiles, ...dirFiles };
// views对象，用于存储路由页面的组件
const views = ref({});
for (const [key, promise] of Object.entries(viewsFiles)) {
	// 提取文件名（去掉路径和扩展名）
	const fileName = key.split('/').pop().split('.')[0];
	// 将组件转换为异步组件并标记为原始对象（防止复用带来的副作用）
	views.value[fileName] = markRaw(defineAsyncComponent({
    loader: () => promise(),
    // 设置组件名
    name: fileName
  }));
}
const vpage = ref(null);//记录hash页
const hashActive = ref(null);
const nowHash = ref(location.hash.slice(1));
const frame = ref(true);//主框架是否显示
const cachedPages = ref([]);
const cachedUrl = ref([]);
const uniqueKey = ref(null);//刷新控制（需处理子组件重复挂载事件）
const pageParams = ref({});
const mainfull = ref(false);
const tagsul = ref();


//setup内方法
onMounted(() => {
	axios({
		url: api_menu,
		method: 'get',
	}).then((res) => {
		var result = res.data;
		if (result.state) {
			store.menuData = result.data;
			store.menuRows = flattenTree(result.data, 'children')
			cachedPages.value = [];
			cachedUrl.value = [];
			pageRouter.refresh()
		}
	}).catch((err) => {
		console.log("按钮菜单加载失败！" + err.status);
	})
});

var btn_ul;

//路由功能
const pageRouter = store.pageRouter = new Router();
pageRouter.init();

//针对全部路由添加监听
pageRouter.route("hash", function (hash, currentUrl, ev) {
	// 含多个路由的数组，component展示最后一个，其它自行处理或单独写route()
	var _hash = hash[hash.length - 1];
	var _pid = '';
	if(_hash.startsWith('pid=')){ //如果是id参数取前面路由
		_pid = _hash.split('=')[1];
		_hash = hash[hash.length - 2];
	}
	var _page = views.value[_hash];
	if (!pageRouter.routes[_hash] || !pageRouter.routes[_hash].sub) { //路由是（/frame/**）显示主框架
		frame.value = hash.length > 1 && hash[0] == 'frame' ? true : false;
	}
	nowHash.value = location.hash.slice(1);
	const menu = store.menuRows.find(item => item.url == nowHash.value);
	hashActive.value = _hash;
	if (_page) {
		// 更新缓存页面列表
		vpage.value = _page; //缓存列表更新后呈现页面
		pageParams.value = { hash: _hash, pid: _pid, ...store.pageParams }
		uniqueKey.value = _hash + _pid;
		updateCachedPages(_hash, _pid, nowHash.value);
		// vpage.value = _page;

	} else if (menu) { //空页则跳转到子页第一个
		const oldHash = ev.oldURL.split("#")[1] || '';
		if (menu.children && menu.children.length>0 && menu.children[0]?.url != oldHash) {
			topage(menu.children[0].url, store.pageParams);
		}
	}

	// ------按钮菜单的折叠------
	nextTick(() => { //等页面class=active变化
		// 以下解决height:auto不支持动画使用max-height固定高度问题
		btn_ul && (btn_ul.style.maxHeight = '0px');
		document.querySelectorAll('.c_left li a').forEach(item => {
			var ul = item.nextElementSibling;
			ul && (ul.style.maxHeight = item.classList.contains('active') ? ul.scrollHeight + 'px' : '0px');
		})
		btn_ul = document.querySelector('.c_left dt.active')?.nextElementSibling;
		btn_ul && (btn_ul.style.maxHeight = btn_ul.scrollHeight + 'px');
		// 标签栏滚动
		const tag = document.querySelector('.pagetags li.active');
		tagsul.value?.scrollTo(tag.getBoundingClientRect().x-tagsul.value.getBoundingClientRect().x+tagsul.value.scrollLeft-tagsul.value.clientWidth*.5+45,0);
	})

});

// 更新缓存的页面列表
function updateCachedPages(hash, pid, url) {
	const index = cachedPages.value.indexOf(hash+pid);
	if (index == -1) {
		cachedUrl.value.push({ hash:hash+pid, url, name: pid?store.title+pid:(store.menuRows.find(item => item.url == url)?.title || hash), params: store.pageParams });
		cachedPages.value.push(hash+pid);
		// 如果缓存页面超过20个，则移除第一个
		if (cachedPages.value.length > 20) {
			wrapperMap.delete(cachedPages.value[0]);
			cachedPages.value.shift();
			cachedUrl.value.shift();
		}
	} else if(url.slice(0,1)=='/'){ //应对地址栏传参变动
		cachedUrl.value[index].url = url;
	}
}
function cachedClose(index) {
	if (cachedPages.value.length == 1) return;
	if(index==-1){ //关闭全部
		wrapperMap.clear();
		cachedPages.value = [];
		cachedUrl.value = [];
		topage(store.homehash);
	}else{
		const key = cachedPages.value[index];
		wrapperMap.delete(key);
		cachedPages.value.splice(index, 1);
		cachedUrl.value.splice(index, 1);
		key == uniqueKey.value && onBack()
	}
}
function refreshPage() {
	// 移除index位的组件，并重新在原位置渲染
	const key = uniqueKey.value;
	const index = cachedPages.value.indexOf(key);
	cachedPages.value.splice(index, 1)
	uniqueKey.value = null;
	nextTick(()=>{
		uniqueKey.value = key;
		cachedPages.value.splice(index, 0, key);
	})
}

// 用来存已经创建的组件
const wrapperMap = new Map();
// 将组件重新渲染为新的组件，原组件包里面
function formatComponent(component, wrapperName) {
	let wrapper;
	if (component) {
		if (wrapperMap.has(wrapperName)) {
			wrapper = wrapperMap.get(wrapperName);
		} else {
			wrapper = {
				name: wrapperName,
				render() {
					return h(component);
				},
			};
			wrapperMap.set(wrapperName, wrapper);
		}
		return h(wrapper);
	}
}

//针对登录路由添加监听
pageRouter.route("login", function (path) {
	hashActive.value = path;
	loginOpen.popup = true;
}, function () {
	loginOpen.popup = false;
});
pageRouter.routes['login'].sub = true;

if (!location.hash) {
	topage(store.homehash);
} else {
	store.pageOpen = location.hash;
}
function onBack() {
	if (location.hash == '#' + store.homehash) {
		return;
	} else if (store.pageOpen == location.hash) {
		store.pageOpen = '';
		location.hash = '#' + store.homehash;
		return;
	}
	window.history.back();
}
store.onBack = onBack;
function btn_a(url) {
	return nowHash.value.startsWith(url);
}

//监听login变化
watch(() => loginOpen.user, (newVal, oldValue) => {
	if (newVal) {
	}
})
function loginClose() {
	if (window.location.hash == '#/login') {
		topage(store.homehash);
	}
}

</script>


<template>

	<div class="wrap" v-if="frame">
		<!-- <div id="menu"></div> -->
		<div :class="['main pile', {mainfull: mainfull}]">
			<div class="c_left wrap">
				<div class="logo-header"><img src="/images/logo.png" alt=""></div>
				<!-- 左侧按钮 -->
				<div class="main innerbox innerscroll">
					<dl v-for="(btn1, index) in store.menuData">
						<dt @click="topage(btn1.url, btn1.params)" :class="{'active':btn_a(btn1.url)}"><svgIcon :name="btn1.ico"></svgIcon>{{btn1.title}}</dt>
						<recursive-menu v-if="btn1.children && btn1.children.length > 0" :menu="btn1.children" :onPage="topage" :btn_a="btn_a"></recursive-menu>
					</dl>
				</div>
				<div class="leftback" @click="mainfull=!mainfull"><svgIcon name="back"></svgIcon></div>
			</div>
			<div class="c_main wrap">
				<div class="tool-header">
					<div id="menu" class="flexrow justify-evenly">
						<li @click="topage('/frame/home')" :class="[{active:hashActive=='home'}]">
							<svgIcon name="home"></svgIcon> 首页
						</li>
						<li @click="topage('/frame/me')" :class="[{active:hashActive=='me'}]">
							<svgIcon name="me"></svgIcon> 我的
						</li>
					</div>
				</div>
				<div id="pagetags" class="flexrow flexstart">
					<div class="pagetags flexrow flexstart">
						<li class="transition" @click="tagsul.scrollTo(tagsul.scrollLeft-300,0)"><</li>
						<ul class="flex1 flexrow innerbox innerscroll" ref="tagsul">
							<li v-for="(item, index) in cachedUrl" :class="[{active:uniqueKey==item.hash},'transition']" @click="topage(item.url, item.params)" :title="item.name">{{ item.name }}<div class="close" @click.stop="cachedClose(index)"></div></li>
						</ul>
						<li class="transition" @click="tagsul.scrollTo(tagsul.scrollLeft+300,0)">></li>
					</div>
					<div class="rightbtn">
						<a @click="refreshPage" title="刷新"><svgIcon name="refresh"></svgIcon></a>
						<a @click="cachedClose(-1)" title="关闭全部">×</a>
					</div>
				</div>
				<div class="main">
					<keep-alive :include="cachedPages">
						<component :is="formatComponent(vpage, uniqueKey)" :hashName="uniqueKey" :pageParams="pageParams" :key="uniqueKey" />
					</keep-alive>
				</div>
			</div>
		</div>

	</div>

	<component v-if="!frame" :is="vpage" :hashName="uniqueKey" :pageParams="pageParams" />

	<el-dialog v-model="loginOpen.popup" @close="loginClose" width="400">
		<login />
	</el-dialog>


</template>

<style>
	#app {
		width: 100%;
		height: 100%;
	}

	.pile {
		overflow: hidden;
	}

</style>